########################################################################
##
## Caution: there are two separate, independent build systems:
## 'makepanda', and 'ppremake'.  Use one or the other, do not attempt
## to use both.  This file is part of the 'makepanda' system.
##
## This file, makepandacore, contains all the global state and
## global functions for the makepanda system.
##
########################################################################

import sys,os,time,stat,string,re,getopt,cPickle,fnmatch,threading,Queue,signal,shutil,platform,glob,getpass,signal
from distutils import sysconfig

SUFFIX_INC=[".cxx",".c",".h",".I",".yxx",".lxx",".mm",".rc",".r",".plist",".idl"]
SUFFIX_DLL=[".dll",".dlo",".dle",".dli",".dlm",".mll",".exe",".pyd",".ocx"]
SUFFIX_LIB=[".lib",".ilb"]
STARTTIME=time.time()
MAINTHREAD=threading.currentThread()
OUTPUTDIR="built"
CUSTOM_OUTPUTDIR=False
THIRDPARTYBASE=None
THIRDPARTYDIR=None
OPTIMIZE="3"
VERBOSE=False
LINK_ALL_STATIC=False

# Is the current Python a 32-bit or 64-bit build?  There doesn't
# appear to be a universal test for this.
if sys.platform == 'darwin':
    # On OSX, platform.architecture reports '64bit' even if it is
    # currently running in 32-bit mode.  But sys.maxint is a reliable
    # indicator.
    is_64 = (sys.maxint > 0x100000000L)
else:
    # On Windows (and Linux?) sys.maxint reports 0x7fffffff even on a
    # 64-bit build.  So we stick with platform.architecture in that
    # case.
    is_64 = (platform.architecture()[0] == '64bit')

########################################################################
##
## Maya and Max Version List (with registry keys)
##
########################################################################

MAYAVERSIONINFO=[("MAYA6",   "6.0"),
                 ("MAYA65",  "6.5"),
                 ("MAYA7",   "7.0"),
                 ("MAYA8",   "8.0"),
                 ("MAYA85",  "8.5"),
                 ("MAYA2008","2008"),
                 ("MAYA2009","2009"),
                 ("MAYA2010","2010"),
                 ("MAYA2011","2011"),
                 ("MAYA2012","2012"),
                 ("MAYA2013","2013"),
]

MAXVERSIONINFO = [("MAX6", "SOFTWARE\\Autodesk\\3DSMAX\\6.0", "installdir", "maxsdk\\cssdk\\include"),
                  ("MAX7", "SOFTWARE\\Autodesk\\3DSMAX\\7.0", "Installdir", "maxsdk\\include\\CS"),
                  ("MAX8", "SOFTWARE\\Autodesk\\3DSMAX\\8.0", "Installdir", "maxsdk\\include\\CS"),
                  ("MAX9", "SOFTWARE\\Autodesk\\3DSMAX\\9.0", "Installdir", "maxsdk\\include\\CS"),
                  ("MAX2009", "SOFTWARE\\Autodesk\\3DSMAX\\11.0\\MAX-1:409", "Installdir", "maxsdk\\include\\CS"),
                  ("MAX2010", "SOFTWARE\\Autodesk\\3DSMAX\\12.0\\MAX-1:409", "Installdir", "maxsdk\\include\\CS"),
                  ("MAX2011", "SOFTWARE\\Autodesk\\3DSMAX\\13.0\\MAX-1:409", "Installdir", "maxsdk\\include\\CS"),
                  ("MAX2012", "SOFTWARE\\Autodesk\\3DSMAX\\14.0\\MAX-1:409", "Installdir", "maxsdk\\include\\CS"),
]

MAYAVERSIONS=[]
MAXVERSIONS=[]
DXVERSIONS=["DX8","DX9"]

for (ver,key) in MAYAVERSIONINFO:
    MAYAVERSIONS.append(ver)

for (ver,key1,key2,subdir) in MAXVERSIONINFO:
    MAXVERSIONS.append(ver)

########################################################################
##
## Potentially Conflicting Files
##
## The next few functions can automatically move away files that
## are commonly generated by ppremake that may conflict with the
## build. When makepanda exits, those files will automatically be
## put back to their original location.
##
########################################################################

CONFLICTING_FILES=["dtool/src/dtoolutil/pandaVersion.h",
                   "dtool/src/dtoolutil/checkPandaVersion.h",
                   "dtool/src/dtoolutil/checkPandaVersion.cxx",
                   "dtool/src/prc/prc_parameters.h",
                   "panda/src/speedtree/speedtree_parameters.h",
                   "direct/src/plugin/p3d_plugin_config.h",
                   "direct/src/plugin_activex/P3DActiveX.rc",
                   "direct/src/plugin_npapi/nppanda3d.rc",
                   "direct/src/plugin_standalone/panda3d.rc"]

def MoveAwayConflictingFiles():
    for cfile in CONFLICTING_FILES:
        if os.path.exists(cfile):
            os.rename(cfile, cfile + ".moved")

def MoveBackConflictingFiles():
    for cfile in CONFLICTING_FILES:
        if os.path.exists(cfile + ".moved"):
            os.rename(cfile + ".moved", cfile)

########################################################################
##
## The exit routine will normally
##
## - print a message
## - save the dependency cache
## - exit
##
## However, if it is invoked inside a thread, it instead:
##
## - prints a message
## - raises the "initiate-exit" exception
##
## If you create a thread, you must be prepared to catch this
## exception, save the dependency cache, and exit.
##
########################################################################

WARNINGS=[]
THREADS={}
HAVE_COLORS=False
SETF=""
try:
  import curses
  curses.setupterm()
  SETF=curses.tigetstr("setf")
  if (SETF == None):
    SETF=curses.tigetstr("setaf")
  assert SETF != None
  HAVE_COLORS=sys.stdout.isatty()
except: pass

def DisableColors():
    global HAVE_COLORS
    HAVE_COLORS = False

def GetColor(color = None):
    if not HAVE_COLORS: return ""
    if color != None: color = color.lower()
    if (color == "blue"):
      return curses.tparm(SETF, 1)
    elif (color == "green"):
      return curses.tparm(SETF, 2)
    elif (color == "cyan"):
      return curses.tparm(SETF, 3)
    elif (color == "red"):
      return curses.tparm(SETF, 4)
    elif (color == "magenta"):
      return curses.tparm(SETF, 5)
    elif (color == "yellow"):
      return curses.tparm(SETF, 6)
    else:
      return curses.tparm(curses.tigetstr("sgr0"))

def PrettyTime(t):
    t = int(t)
    hours = t/3600
    t -= hours*3600
    minutes = t/60
    t -= minutes*60
    seconds = t
    if (hours): return str(hours)+" hours "+str(minutes)+" min"
    if (minutes): return str(minutes)+" min "+str(seconds)+" sec"
    return str(seconds)+" sec"

def ProgressOutput(progress, msg, target = None):
    if (threading.currentThread() == MAINTHREAD):
        if (progress == None):
            print msg
        elif (progress >= 100.0):
            print "%s[%s%d%%%s] %s" % (GetColor("yellow"), GetColor("cyan"), progress, GetColor("yellow"), msg),
        elif (progress < 10.0):
            print "%s[%s  %d%%%s] %s" % (GetColor("yellow"), GetColor("cyan"), progress, GetColor("yellow"), msg),
        else:
            print "%s[%s %d%%%s] %s" % (GetColor("yellow"), GetColor("cyan"), progress, GetColor("yellow"), msg),
    else:
        global THREADS
        ident = threading.currentThread().ident
        if (ident not in THREADS):
            THREADS[ident] = len(THREADS) + 1
        print "%s[%sT%d%s] %s" % (GetColor("yellow"), GetColor("cyan"), THREADS[ident], GetColor("yellow"), msg),
    if (target == None):
        print GetColor()
    else:
        print "%s%s%s" % (GetColor("green"), target, GetColor())

def exit(msg = ""):
    sys.stdout.flush()
    sys.stderr.flush()
    if (threading.currentThread() == MAINTHREAD):
        SaveDependencyCache()
        MoveBackConflictingFiles()
        print "Elapsed Time: "+PrettyTime(time.time() - STARTTIME)
        print msg
        print GetColor("red") + "Build terminated." + GetColor()
        sys.stdout.flush()
        sys.stderr.flush()
        ##Don't quit the interperter if I'm running this file directly (debugging)
        if __name__ != '__main__':
            os._exit(1)
    else:
        print msg
        raise "initiate-exit"

########################################################################
##
## LocateBinary
##
## This function searches the system PATH for the binary. Returns its
## full path when it is found, or None when it was not found.
##
########################################################################

def LocateBinary(binary):
    if os.path.isfile(binary):
        return binary
    if "PATH" not in os.environ or os.environ["PATH"] == "":
        p = os.defpath
    else:
        p = os.environ["PATH"]

    for path in p.split(os.pathsep):
        binpath = os.path.join(os.path.expanduser(path), binary)
        if os.access(binpath, os.X_OK):
            return os.path.abspath(os.path.realpath(binpath))
    return None

########################################################################
##
## Run a command.
##
########################################################################

def oscmd(cmd, ignoreError = False):
    if VERBOSE:
        print GetColor("blue") + cmd.split(" ", 1)[0] + " " + GetColor("magenta") + cmd.split(" ", 1)[1] + GetColor()
    sys.stdout.flush()
    if sys.platform == "win32":
        exe = cmd.split()[0]
        if not (len(exe) > 4 and exe[-4:] == ".exe"):
            exe += ".exe"
        if os.path.isfile(exe)==0:
            for i in os.environ["PATH"].split(";"):
                if os.path.isfile(os.path.join(i, exe)):
                    exe = os.path.join(i, exe)
                    break
            if os.path.isfile(exe)==0:
                exit("Cannot find "+exe+" on search path")
        res = os.spawnl(os.P_WAIT, exe, cmd)
    else:
        res = os.system(cmd)
        sig = res & 0x7F
        if (GetVerbose() and res != 0):
            print GetColor("red") + "Process exited with exit status %d and signal code %d" % ((res & 0xFF00) >> 8, sig) + GetColor()
        if (sig == signal.SIGINT):
            exit("keyboard interrupt")
        # Don't ask me where the 35584 or 34304 come from...
        if (sig == signal.SIGSEGV or res == 35584 or res == 34304):
            if (LocateBinary("gdb") and GetVerbose()):
                print GetColor("red") + "Received SIGSEGV, retrieving traceback..." + GetColor()
                os.system("gdb -batch -ex 'handle SIG33 pass nostop noprint' -ex 'set pagination 0' -ex 'run' -ex 'bt full' -ex 'info registers' -ex 'thread apply all backtrace' -ex 'quit' --args %s < /dev/null" % cmd)
            else:
                print GetColor("red") + "Received SIGSEGV" + GetColor()
            exit("")
    if res != 0 and not ignoreError:
        if "interrogate" in cmd.split(" ", 1)[0] and GetVerbose():
            print GetColor("red") + "Interrogate failed, retrieving debug output..." + GetColor()
            if sys.platform == "win32":
                os.spawnl(os.P_WAIT, exe, cmd.split(" ", 1)[0] + " -v " + cmd.split(" ", 1)[1])
            else:
                os.system(cmd.split(" ", 1)[0] + " -v " + cmd.split(" ", 1)[1])
        exit("The following command returned a non-zero value: " + str(cmd))
    return res

########################################################################
##
## GetDirectoryContents
##
## At times, makepanda will use a function like "os.listdir" to process
## all the files in a directory.  Unfortunately, that means that any
## accidental addition of a file to a directory could cause makepanda
## to misbehave without warning.
##
## To alleviate this weakness, we created GetDirectoryContents.  This
## uses "os.listdir" to fetch the directory contents, but then it
## compares the results to the appropriate CVS/Entries to see if
## they match.  If not, it prints a big warning message.
##
########################################################################

def GetDirectoryContents(dir, filters="*", skip=[]):
    if (type(filters)==str):
        filters = [filters]
    actual = {}
    files = os.listdir(dir)
    for filter in filters:
        for file in fnmatch.filter(files, filter):
            if (skip.count(file)==0) and (os.path.isfile(dir + "/" + file)):
                actual[file] = 1
    if (os.path.isfile(dir + "/CVS/Entries")):
        cvs = {}
        srchandle = open(dir+"/CVS/Entries", "r")
        files = []
        for line in srchandle:
            if (line[0]=="/"):
                s = line.split("/",2)
                if (len(s)==3):
                    files.append(s[1])
        srchandle.close()
        for filter in filters:
            for file in fnmatch.filter(files, filter):
                if (skip.count(file)==0):
                    cvs[file] = 1
        for file in actual.keys():
            if (file not in cvs and VERBOSE):
                msg = "%sWARNING: %s is in %s, but not in CVS%s" % (GetColor("red"), GetColor("green") + file + GetColor(), GetColor("green") + dir + GetColor(), GetColor())
                print msg
                WARNINGS.append(msg)
        for file in cvs.keys():
            if (file not in actual and VERBOSE):
                msg = "%sWARNING: %s is not in %s, but is in CVS%s" % (GetColor("red"), GetColor("green") + file + GetColor(), GetColor("green") + dir + GetColor(), GetColor())
                print msg
                WARNINGS.append(msg)
    results = actual.keys()
    results.sort()
    return results

def GetDirectorySize(dir):
    size = 0
    for (path, dirs, files) in os.walk(dir):
        for file in files:
            try:
                size += os.path.getsize(os.path.join(path, file))
            except: pass
    return size

########################################################################
##
## The Timestamp Cache
##
## The make utility is constantly fetching the timestamps of files.
## This can represent the bulk of the file accesses during the make
## process.  The timestamp cache eliminates redundant checks.
##
########################################################################

TIMESTAMPCACHE = {}

def GetTimestamp(path):
    if path in TIMESTAMPCACHE:
        return TIMESTAMPCACHE[path]
    try: date = os.path.getmtime(path)
    except: date = 0
    TIMESTAMPCACHE[path] = date
    return date

def ClearTimestamp(path):
    del TIMESTAMPCACHE[path]

########################################################################
##
## The Dependency cache.
##
## Makepanda's strategy for file dependencies is different from most
## make-utilities.  Whenever a file is built, makepanda records
## that the file was built, and it records what the input files were,
## and what their dates were.  Whenever a file is about to be built,
## panda compares the current list of input files and their dates,
## to the previous list of input files and their dates.  If they match,
## there is no need to build the file.
##
########################################################################

BUILTFROMCACHE = {}

def JustBuilt(files,others):
    dates = []
    for file in files:
        del TIMESTAMPCACHE[file]
        dates.append(GetTimestamp(file))
    for file in others:
        dates.append(GetTimestamp(file))
    key = tuple(files)
    BUILTFROMCACHE[key] = [others,dates]

def NeedsBuild(files,others):
    dates = []
    for file in files:
        dates.append(GetTimestamp(file))
        if (not os.path.exists(file)): return 1
    for file in others:
        dates.append(GetTimestamp(file))
    key = tuple(files)
    if (key in BUILTFROMCACHE):
        if (BUILTFROMCACHE[key] == [others,dates]):
            return 0
        else:
            oldothers = BUILTFROMCACHE[key][0]
            if (oldothers != others and VERBOSE):
                print "%sWARNING:%s file dependencies changed: %s%s%s" % (GetColor("red"), GetColor(), GetColor("green"), str(files), GetColor())
    return 1

########################################################################
##
## The CXX include cache:
##
## The following routine scans a CXX file and returns a list of
## the include-directives inside that file.  It's not recursive:
## it just returns the includes that are textually inside the
## file.  If you need recursive dependencies, you need the higher-level
## routine CxxCalcDependencies, defined elsewhere.
##
## Since scanning a CXX file is slow, we cache the result.  It records
## the date of the source file and the list of includes that it
## contains.  It assumes that if the file date hasn't changed, that
## the list of include-statements inside the file has not changed
## either.  Once again, this particular routine does not return
## recursive dependencies --- it only returns an explicit list of
## include statements that are textually inside the file.  That
## is what the cache stores, as well.
##
########################################################################

CXXINCLUDECACHE = {}

global CxxIncludeRegex
CxxIncludeRegex = re.compile('^[ \t]*[#][ \t]*include[ \t]+"([^"]+)"[ \t\r\n]*$')

def CxxGetIncludes(path):
    date = GetTimestamp(path)
    if (path in CXXINCLUDECACHE):
        cached = CXXINCLUDECACHE[path]
        if (cached[0]==date): return cached[1]
    try: sfile = open(path, 'rb')
    except:
        exit("Cannot open source file \""+path+"\" for reading.")
    include = []
    for line in sfile:
        match = CxxIncludeRegex.match(line,0)
        if (match):
            incname = match.group(1)
            include.append(incname)
    sfile.close()
    CXXINCLUDECACHE[path] = [date, include]
    return include

########################################################################
##
## SaveDependencyCache / LoadDependencyCache
##
## This actually saves both the dependency and cxx-include caches.
##
########################################################################

DCACHE_BACKED_UP = False

def SaveDependencyCache():
    global DCACHE_BACKED_UP
    if not DCACHE_BACKED_UP:
        try:
            if (os.path.exists(os.path.join(OUTPUTDIR, "tmp", "makepanda-dcache"))):
                os.rename(os.path.join(OUTPUTDIR, "tmp", "makepanda-dcache"),
                          os.path.join(OUTPUTDIR, "tmp", "makepanda-dcache-backup"))
        except: pass
        DCACHE_BACKED_UP = True
    try: icache = open(os.path.join(OUTPUTDIR, "tmp", "makepanda-dcache"),'wb')
    except: icache = 0
    if (icache!=0):
        print "Storing dependency cache."
        cPickle.dump(CXXINCLUDECACHE, icache, 1)
        cPickle.dump(BUILTFROMCACHE, icache, 1)
        icache.close()

def LoadDependencyCache():
    global CXXINCLUDECACHE
    global BUILTFROMCACHE
    try: icache = open(os.path.join(OUTPUTDIR, "tmp", "makepanda-dcache"),'rb')
    except: icache = 0
    if (icache!=0):
        CXXINCLUDECACHE = cPickle.load(icache)
        BUILTFROMCACHE = cPickle.load(icache)
        icache.close()

########################################################################
##
## CxxFindSource: given a source file name and a directory list,
## searches the directory list for the given source file.  Returns
## the full pathname of the located file.
##
## CxxFindHeader: given a source file, an include directive, and a
## directory list, searches the directory list for the given header
## file.  Returns the full pathname of the located file.
##
## Of course, CxxFindSource and CxxFindHeader cannot find a source
## file that has not been created yet.  This can cause dependency
## problems.  So the function CreateStubHeader can be used to create
## a file that CxxFindSource or CxxFindHeader can subsequently find.
##
########################################################################

def CxxFindSource(name, ipath):
    for dir in ipath:
        if (dir == "."): full = name
        else: full = dir + "/" + name
        if GetTimestamp(full) > 0: return full
    exit("Could not find source file: "+name)

def CxxFindHeader(srcfile, incfile, ipath):
    if (incfile.startswith(".")):
        last = srcfile.rfind("/")
        if (last < 0): exit("CxxFindHeader cannot handle this case #1")
        srcdir = srcfile[:last+1]
        while (incfile[:1]=="."):
            if (incfile[:2]=="./"):
                incfile = incfile[2:]
            elif (incfile[:3]=="../"):
                incfile = incfile[3:]
                last = srcdir[:-1].rfind("/")
                if (last < 0): exit("CxxFindHeader cannot handle this case #2")
                srcdir = srcdir[:last+1]
            else: exit("CxxFindHeader cannot handle this case #3")
        full = srcdir + incfile
        if GetTimestamp(full) > 0: return full
        return 0
    else:
        for dir in ipath:
            full = dir + "/" + incfile
            if GetTimestamp(full) > 0: return full
        return 0

########################################################################
##
## CxxCalcDependencies(srcfile, ipath, ignore)
##
## Calculate the dependencies of a source file given a
## particular include-path.  Any file in the list of files to
## ignore is not considered.
##
########################################################################

global CxxIgnoreHeader
global CxxDependencyCache
CxxIgnoreHeader = {}
CxxDependencyCache = {}

def CxxCalcDependencies(srcfile, ipath, ignore):
    if (srcfile in CxxDependencyCache):
        return CxxDependencyCache[srcfile]
    if (ignore.count(srcfile)): return []
    dep = {}
    dep[srcfile] = 1
    includes = CxxGetIncludes(srcfile)
    for include in includes:
        header = CxxFindHeader(srcfile, include, ipath)
        if (header!=0):
            if (ignore.count(header)==0):
                hdeps = CxxCalcDependencies(header, ipath, [srcfile]+ignore)
                for x in hdeps: dep[x] = 1
    result = dep.keys()
    CxxDependencyCache[srcfile] = result
    return result

########################################################################
##
## Registry Key Handling
##
## Of course, these routines will fail if you call them on a
## non win32 platform.  If you use them on a win64 platform, they
## will look in the win32 private hive first, then look in the
## win64 hive.
##
########################################################################

if sys.platform == "win32":
    import _winreg

def TryRegistryKey(path):
    try:
        key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, path, 0, _winreg.KEY_READ)
        return key
    except: pass
    try:
        key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, path, 0, _winreg.KEY_READ | 256)
        return key
    except: pass
    return 0

def ListRegistryKeys(path):
    result=[]
    index=0
    key = TryRegistryKey(path)
    if (key != 0):
        try:
            while (1):
                result.append(_winreg.EnumKey(key, index))
                index = index + 1
        except: pass
        _winreg.CloseKey(key)
    return result

def ListRegistryValues(path):
    result = []
    index = 0
    key = TryRegistryKey(path)
    if (key != 0):
        try:
            while (1):
                result.append(_winreg.EnumValue(key, index)[0])
                index = index + 1
        except: pass
        _winreg.CloseKey(key)
    return result

def GetRegistryKey(path, subkey, override64=True):
    if (is_64 and override64==True):
        path = path.replace("SOFTWARE\\", "SOFTWARE\\Wow6432Node\\")
    k1=0
    key = TryRegistryKey(path)
    if (key != 0):
        try:
            k1, k2 = _winreg.QueryValueEx(key, subkey)
        except: pass
        _winreg.CloseKey(key)
    return k1

def GetProgramFiles():
    if ("PROGRAMFILES" in os.environ):
        return os.environ["PROGRAMFILES"]
    elif (os.path.isdir("C:\\Program Files")):
        return "C:\\Program Files"
    elif (os.path.isdir("D:\\Program Files")):
        return "D:\\Program Files"
    elif (os.path.isdir("E:\\Program Files")):
        return "E:\\Program Files"
    return 0

########################################################################
##
## Parsing Compiler Option Lists
##
########################################################################

def GetListOption(opts, prefix):
    res=[]
    for x in opts:
        if (x.startswith(prefix)):
            res.append(x[len(prefix):])
    return res

def GetValueOption(opts, prefix):
    for x in opts:
        if (x.startswith(prefix)):
            return x[len(prefix):]
    return 0

def GetOptimizeOption(opts):
    val = GetValueOption(opts, "OPT:")
    if (val == 0):
        return OPTIMIZE
    return val

########################################################################
##
## General File Manipulation
##
########################################################################

def MakeDirectory(path):
    if os.path.isdir(path): return 0
    os.mkdir(path)

def ReadFile(wfile):
    try:
        srchandle = open(wfile, "rb")
        data = srchandle.read()
        srchandle.close()
        return data
    except: exit("Cannot read "+wfile)

def WriteFile(wfile,data):
    try:
        dsthandle = open(wfile, "wb")
        dsthandle.write(data)
        dsthandle.close()
    except: exit("Cannot write "+wfile)

def ConditionalWriteFile(dest,desiredcontents):
    try:
        rfile = open(dest, 'rb')
        contents = rfile.read(-1)
        rfile.close()
    except:
        contents=0
    if contents != desiredcontents:
        sys.stdout.flush()
        WriteFile(dest,desiredcontents)

def DeleteCVS(dir):
    if dir == "": dir = "."
    for entry in os.listdir(dir):
        subdir = os.path.join(dir, entry)
        if (os.path.isdir(subdir)):
            if (entry == "CVS" or entry == "CVSROOT"):
                shutil.rmtree(subdir)
            else:
                DeleteCVS(subdir)
        elif (os.path.isfile(subdir) and (entry == ".cvsignore" or entry.startswith(".#"))):
            os.remove(subdir)

def DeleteBuildFiles(dir):
    if dir == "": dir = "."
    for entry in os.listdir(dir):
        subdir = os.path.join(dir, entry)
        if (os.path.isfile(subdir) and os.path.splitext(subdir)[-1] in [".pp", ".moved"]):
            os.remove(subdir)
        elif (os.path.isdir(subdir)):
            if (os.path.basename(subdir)[:3] == "Opt" and os.path.basename(subdir)[4] == "-"):
                shutil.rmtree(subdir)
            else:
                DeleteBuildFiles(subdir)

def DeleteEmptyDirs(dir):
    if dir == "": dir = "."
    entries = os.listdir(dir)
    if not entries:
        os.rmdir(dir)
        return
    for entry in entries:
        subdir = os.path.join(dir, entry)
        if (os.path.isdir(subdir)):
            if (not os.listdir(subdir)):
                os.rmdir(subdir)
            else:
                DeleteEmptyDirs(subdir)

def CreateFile(file):
    if (os.path.exists(file)==0):
        WriteFile(file,"")

########################################################################
#
# Create the panda build tree.
#
########################################################################

def MakeBuildTree():
    MakeDirectory(OUTPUTDIR)
    MakeDirectory(OUTPUTDIR+"/bin")
    MakeDirectory(OUTPUTDIR+"/lib")
    MakeDirectory(OUTPUTDIR+"/tmp")
    MakeDirectory(OUTPUTDIR+"/etc")
    MakeDirectory(OUTPUTDIR+"/plugins")
    MakeDirectory(OUTPUTDIR+"/include")
    MakeDirectory(OUTPUTDIR+"/models")
    MakeDirectory(OUTPUTDIR+"/models/audio")
    MakeDirectory(OUTPUTDIR+"/models/audio/sfx")
    MakeDirectory(OUTPUTDIR+"/models/icons")
    MakeDirectory(OUTPUTDIR+"/models/maps")
    MakeDirectory(OUTPUTDIR+"/models/misc")
    MakeDirectory(OUTPUTDIR+"/models/gui")
    MakeDirectory(OUTPUTDIR+"/pandac")
    MakeDirectory(OUTPUTDIR+"/pandac/input")

########################################################################
#
# Make sure that you are in the root of the panda tree.
#
########################################################################

def CheckPandaSourceTree():
    dir = os.getcwd()
    if ((os.path.exists(os.path.join(dir, "makepanda/makepanda.py"))==0) or
        (os.path.exists(os.path.join(dir, "dtool","src","dtoolbase","dtoolbase.h"))==0) or
        (os.path.exists(os.path.join(dir, "panda","src","pandabase","pandabase.h"))==0)):
        exit("Current directory is not the root of the panda tree.")

########################################################################
##
## Thirdparty libraries paths
##
########################################################################

def GetThirdpartyBase():
    global THIRDPARTYBASE
    if (THIRDPARTYBASE != None):
        return THIRDPARTYBASE

    THIRDPARTYBASE = "thirdparty"
    if "MAKEPANDA_THIRDPARTY" in os.environ:
        THIRDPARTYBASE = os.environ["MAKEPANDA_THIRDPARTY"]
    return THIRDPARTYBASE

def GetThirdpartyDir():
    global THIRDPARTYDIR
    if (THIRDPARTYDIR != None):
        return THIRDPARTYDIR
    if (sys.platform.startswith("win")):
        if (is_64):
            THIRDPARTYDIR=GetThirdpartyBase()+"/win-libs-vc9-x64/"
        else:
            THIRDPARTYDIR=GetThirdpartyBase()+"/win-libs-vc9/"
        if not os.path.isdir(THIRDPARTYDIR):
            THIRDPARTYDIR=GetThirdpartyBase()+"/win-libs-vc9/"
    elif (sys.platform == "darwin"):
        THIRDPARTYDIR=GetThirdpartyBase()+"/darwin-libs-a/"
    elif (sys.platform.startswith("linux")):
        if (platform.machine().startswith("arm")):
            THIRDPARTYDIR=GetThirdpartyBase()+"/linux-libs-arm/"
        elif (is_64):
            THIRDPARTYDIR=GetThirdpartyBase()+"/linux-libs-x64/"
        else:
            THIRDPARTYDIR=GetThirdpartyBase()+"/linux-libs-a/"
    elif (sys.platform.startswith("freebsd")):
        if (platform.machine().startswith("arm")):
            THIRDPARTYDIR=GetThirdpartyBase()+"/freebsd-libs-arm/"
        elif (is_64):
            THIRDPARTYDIR=GetThirdpartyBase()+"/freebsd-libs-x64/"
        else:
            THIRDPARTYDIR=GetThirdpartyBase()+"/freebsd-libs-a/"
    else:
        print GetColor("red") + "WARNING:" + GetColor("Unsupported platform: " + sys.platform)
    return THIRDPARTYDIR

########################################################################
##
## Visual Studio Manifest Manipulation.
## These functions exist to make sure we are referencing the same
## version of the VC runtime in the manifests that we are shipping
## with Panda3D, to avoid side-by-side configuration errors. Also,
## it also removes any dependency the VC80 CRT (as we only want to
## depend on the VC90 CRT).
##
########################################################################

VC90CRTVERSIONRE=re.compile("name=['\"]Microsoft.VC90.CRT['\"]\\s+version=['\"]([0-9.]+)['\"]")
VC80CRTASSEMBLYRE=re.compile("<dependency>[\t \r\n]*<dependentAssembly>[\t \r\n]*<assemblyIdentity[\t \r\na-zA-Z0-9.'\"=]*name=['\"]Microsoft[.]VC80[.]CRT['\"][\t \r\na-zA-Z0-9.'\"=/]*>[\t \r\n]*(</assemblyIdentity>)?[\t \r\n]*</dependentAssembly>[\t \r\n]*</dependency>[\t \r\n]*")
VC90CRTVERSION=None

def GetVC90CRTVersion(fn = None):
    global VC90CRTVERSION
    if (VC90CRTVERSION != None):
        return VC90CRTVERSION
    if (not sys.platform.startswith("win")):
        VC90CRTVERSION = 0
        return 0
    if (fn == None):
        fn = GetThirdpartyDir()+"extras/bin/Microsoft.VC90.CRT.manifest"
    manifest = ReadFile(fn)
    version = VC90CRTVERSIONRE.search(manifest)
    if (version == None):
        exit("Cannot locate version number in "+fn)
    VC90CRTVERSION = version.group(1)
    return version.group(1)

def SetVC90CRTVersion(fn, ver = None):
    if (ver == None): ver = GetVC90CRTVersion()
    manifest = ReadFile(fn)
    subst = " name='Microsoft.VC90.CRT' version='"+ver+"' "
    manifest = VC90CRTVERSIONRE.sub(subst, manifest)
    manifest = VC80CRTASSEMBLYRE.sub("", manifest)
    WriteFile(fn, manifest)

########################################################################
##
## Gets or sets the output directory, by default "built".
## Gets or sets the optimize level.
## Gets or sets the verbose flag.
##
########################################################################

def GetOutputDir():
  return OUTPUTDIR

def IsCustomOutputDir():
  return CUSTOM_OUTPUTDIR

def SetOutputDir(outputdir):
  global OUTPUTDIR, CUSTOM_OUTPUTDIR
  OUTPUTDIR=outputdir
  CUSTOM_OUTPUTDIR=True

def GetOptimize():
  return int(OPTIMIZE)

def SetOptimize(optimize):
  global OPTIMIZE
  OPTIMIZE=optimize

def GetVerbose():
  return VERBOSE

def SetVerbose(verbose):
  global VERBOSE
  VERBOSE=verbose

def GetLinkAllStatic():
  return LINK_ALL_STATIC

def SetLinkAllStatic(val = True):
  global LINK_ALL_STATIC
  LINK_ALL_STATIC = val

def UnsetLinkAllStatic():
  global LINK_ALL_STATIC
  LINK_ALL_STATIC = False

########################################################################
##
## Package Selection
##
## This facility enables makepanda to keep a list of packages selected
## by the user for inclusion or omission.
##
########################################################################

PKG_LIST_ALL=[]
PKG_LIST_OMIT={}

def PkgListSet(pkgs):
    global PKG_LIST_ALL
    global PKG_LIST_OMIT
    PKG_LIST_ALL=pkgs
    PKG_LIST_OMIT={}
    PkgEnableAll()

def PkgListGet():
    return PKG_LIST_ALL

def PkgEnableAll():
    for x in PKG_LIST_ALL:
        PKG_LIST_OMIT[x] = 0

def PkgDisableAll():
    for x in PKG_LIST_ALL:
        PKG_LIST_OMIT[x] = 1

def PkgEnable(pkg):
    PKG_LIST_OMIT[pkg] = 0

def PkgDisable(pkg):
    PKG_LIST_OMIT[pkg] = 1

def PkgSkip(pkg):
    return PKG_LIST_OMIT[pkg]

def PkgSelected(pkglist, pkg):
    if (pkglist.count(pkg)==0): return 0
    if (PKG_LIST_OMIT[pkg]): return 0
    return 1

########################################################################
##
## DTOOL/PRC Option value override
##
## This facility enables makepanda to keep a list of parameters
## overriden by the command line.
##
########################################################################

OVERRIDES_LIST={}

def AddOverride(spec):
    if (spec.find("=")==-1):return
    pair = spec.split("=",1)
    OVERRIDES_LIST[pair[0]] = pair[1]

def OverrideValue(parameter, value):
    if parameter in OVERRIDES_LIST:
        print "Overriding value of key \"" + parameter + "\" with value \"" + OVERRIDES_LIST[parameter] + "\""
        return OVERRIDES_LIST[parameter]
    else:
        return value

########################################################################
##
## These functions are for libraries which use pkg-config.
##
########################################################################

def PkgConfigHavePkg(pkgname, tool = "pkg-config"):
    """Returns a bool whether the pkg-config package is installed."""

    if (sys.platform == "win32" or not LocateBinary(tool)):
        return False
    if (tool == "pkg-config"):
        handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --modversion " + pkgname)
    else:
        return bool(LocateBinary(tool) != None)
    result = handle.read().strip()
    returnval = handle.close()
    if returnval != None and returnval != 0:
        return False
    return bool(len(result) > 0)

def PkgConfigGetLibs(pkgname, tool = "pkg-config"):
    """Returns a list of libs for the package, prefixed by -l."""

    if (sys.platform == "win32" or not LocateBinary(tool)):
        return []
    if (tool == "pkg-config"):
        handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --libs-only-l " + pkgname)
    elif (tool == "fltk-config"):
        handle = os.popen(LocateBinary("fltk-config") + " --ldstaticflags")
    else:
        handle = os.popen(LocateBinary(tool) + " --libs")
    result = handle.read().strip()
    handle.close()
    libs = []

    # Walk through the result arguments carefully.  Look for -lname as
    # well as -framework name.
    r = result.split(' ')
    ri = 0
    while ri < len(r):
        l = r[ri]
        if l.startswith("-l") or l.startswith("/"):
            libs.append(l)
        elif l == '-framework':
            libs.append(l)
            ri += 1
            libs.append(r[ri])
        ri += 1

    return libs

def PkgConfigGetIncDirs(pkgname, tool = "pkg-config"):
    """Returns a list of includes for the package, NOT prefixed by -I."""

    if (sys.platform == "win32" or not LocateBinary(tool)):
        return []
    if (tool == "pkg-config"):
        handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --cflags-only-I " + pkgname)
    else:
        handle = os.popen(LocateBinary(tool) + " --cflags")
    result = handle.read().strip()
    if len(result) == 0: return []
    handle.close()
    dirs = []
    for opt in result.split(" "):
        if (opt.startswith("-I")):
            inc_dir = opt.replace("-I", "").replace("\"", "").strip()
            # Hack for ODE, otherwise -S/usr/include gets added to interrogate
            if inc_dir != '/usr/include' and inc_dir != '/usr/include/':
                dirs.append(inc_dir)
    return dirs

def PkgConfigGetLibDirs(pkgname, tool = "pkg-config"):
    """Returns a list of library paths for the package, NOT prefixed by -L."""

    if (sys.platform == "win32" or not LocateBinary(tool)):
        return []
    if (tool == "pkg-config"):
        handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --libs-only-L " + pkgname)
    elif (tool == "wx-config" or tool == "ode-config"):
        return []
    else:
        handle = os.popen(LocateBinary(tool) + " --ldflags")
    result = handle.read().strip()
    handle.close()
    if len(result) == 0: return []
    libs = []
    for l in result.split(" "):
        if (l.startswith("-L")):
            libs.append(l.replace("-L", "").replace("\"", "").strip())
    return libs

def PkgConfigGetDefSymbols(pkgname, tool = "pkg-config"):
    """Returns a dictionary of preprocessor definitions."""

    if (sys.platform == "win32" or not LocateBinary(tool)):
        return []
    if (tool == "pkg-config"):
        handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --cflags " + pkgname)
    else:
        handle = os.popen(LocateBinary(tool) + " --cflags")
    result = handle.read().strip()
    handle.close()
    if len(result) == 0: return {}
    defs = {}
    for l in result.split(" "):
        if (l.startswith("-D")):
            d = l.replace("-D", "").replace("\"", "").strip().split("=")
            if (len(d) == 1):
                defs[d[0]] = ""
            else:
                defs[d[0]] = d[1]
    return defs

def PkgConfigEnable(opt, pkgname, tool = "pkg-config"):
    """Adds the libraries and includes to IncDirectory, LibName and LibDirectory."""
    for i in PkgConfigGetIncDirs(pkgname, tool):
        IncDirectory(opt, i)
    for i in PkgConfigGetLibDirs(pkgname, tool):
        LibDirectory(opt, i)
    for i in PkgConfigGetLibs(pkgname, tool):
        LibName(opt, i)
    for i, j in PkgConfigGetDefSymbols(pkgname, tool).items():
        DefSymbol(opt, i, j)

LD_CACHE = None
STATIC_CACHE = None

def GetLibCache():
    # Returns a list of cached libraries, not prefixed by lib and not suffixed by .so* or .a!
    # We should probably replace this function with something that uses gcc -dump-search-dirs
    global LD_CACHE
    if (LD_CACHE == None):
        LD_CACHE = []
        print "Generating library cache..."
        if (LocateBinary("ldconfig") != None and not sys.platform.startswith("freebsd")):
            handle = os.popen(LocateBinary("ldconfig") + " -NXp")
            result = handle.read().strip().split("\n")
            for line in result:
                lib = line.strip().split(" ", 1)[0]
                if (lib.endswith(".so") or ".so " in lib):
                    lib = lib.split(".so", 1)[0][3:]
                    LD_CACHE.append(lib)

        libdirs = ["/lib", "/usr/lib", "/usr/local/lib", "/usr/PCBSD/local/lib", "/usr/X11/lib", "/usr/X11R6/lib"]
        if is_64:
            libdirs += ["/lib64", "/usr/lib64"]

        # Multilib location for more recent Linux distros
        # This is a hack, we should probably use 'gcc -dumpmachine' or so
        machine = platform.machine().lower()
        if machine == 'i686':
            machine = 'i386'
        libdirs += ["/lib/%s-linux-gnu" % machine, "/usr/lib/%s-linux-gnu" % machine, "/usr/local/lib/%s-linux-gnu" % machine]

        if "LD_LIBRARY_PATH" in os.environ:
            libdirs += os.environ["LD_LIBRARY_PATH"].split(":")
        libdirs = list(set(libdirs))
        libs = []
        for path in libdirs:
            if os.path.isdir(path):
                libs += glob.glob(path + "/lib*.so")
                libs += glob.glob(path + "/lib*.a")
                if (sys.platform == "darwin"):
                    libs += glob.glob(path + "/lib*.dylib")

        for l in libs:
            lib = os.path.basename(l).split(".so", 1)[0].split(".a", 1)[0].split(".dylib", 1)[0][3:]
            if lib not in LD_CACHE:
                LD_CACHE.append(lib)
    return LD_CACHE

def ChooseLib(*libs):
    # Chooses a library from the parameters, in order of preference. Returns the first if none of them were found.
    for l in libs:
        libname = l
        if (l.startswith("lib")):
            libname = l[3:]
        if (libname in GetLibCache()):
            return libname
    if (len(libs) > 0):
        if (VERBOSE):
            print GetColor("cyan") + "Couldn't find any of the libraries " + ", ".join(libs) + GetColor()
        return libs[0]

def SmartPkgEnable(pkg, pkgconfig = None, libs = None, incs = None, defs = None, framework = None, target_pkg = None, tool = "pkg-config"):
    global PKG_LIST_ALL
    if (pkg in PkgListGet() and PkgSkip(pkg)):
        return
    if (target_pkg == "" or target_pkg == None):
        target_pkg = pkg
    if (pkgconfig == ""):
        pkgconfig = None
    if (framework == ""):
        framework = None
    if (libs == None or libs == ""):
        libs = ()
    elif (isinstance(libs, str)):
        libs = (libs, )
    if (incs == None or incs == ""):
        incs = ()
    elif (isinstance(incs, str)):
        incs = (incs, )
    if (defs == None or defs == "" or len(defs) == 0):
        defs = {}
    elif (isinstance(incs, str)):
        defs = {defs : ""}
    elif (isinstance(incs, list) or isinstance(incs, tuple) or isinstance(incs, set)):
        olddefs = defs
        defs = {}
        for d in olddefs:
            defs[d] = ""

    if (pkg.lower() == "swscale" and os.path.isfile(GetThirdpartyDir() + "ffmpeg/include/libswscale/swscale.h")):
        # Let it be handled by the ffmpeg package
        LibName(target_pkg, "-lswscale")
        return

    if (os.path.isdir(GetThirdpartyDir() + pkg.lower())):
        IncDirectory(target_pkg, GetThirdpartyDir() + pkg.lower() + "/include")
        LibDirectory(target_pkg, GetThirdpartyDir() + pkg.lower() + "/lib")
        if (PkgSkip("PYTHON") == 0):
            LibDirectory(target_pkg, GetThirdpartyDir() + pkg.lower() + "/lib/" + SDK["PYTHONVERSION"])

        # TODO: check for a .pc file in the lib/pkg-config/ dir
        if (tool != None and os.path.isfile(GetThirdpartyDir() + pkg.lower() + "/bin/" + tool)):
            tool = GetThirdpartyDir() + pkg.lower() + "/bin/" + tool
            for i in PkgConfigGetLibs(None, tool):
                LibName(target_pkg, i)
            for i, j in PkgConfigGetDefSymbols(None, tool).items():
                DefSymbol(target_pkg, i, j)
            return

        for l in libs:
            libname = l
            if (l.startswith("lib")):
                libname = l[3:]
            # This is for backward compatibility - in the thirdparty dir, we kept some libs with "panda" prefix, like libpandatiff.
            if (len(glob.glob(GetThirdpartyDir() + pkg.lower() + "/lib/libpanda%s.*" % libname)) > 0 and
                len(glob.glob(GetThirdpartyDir() + pkg.lower() + "/lib/lib%s.*" % libname)) == 0):
                libname = "panda" + libname
            LibName(target_pkg, "-l" + libname)

        for d, v in defs.values():
            DefSymbol(target_pkg, d, v)
        return
    elif (sys.platform == "darwin" and framework != None):
        if (os.path.isdir("/Library/Frameworks/%s.framework" % framework) or
            os.path.isdir("/System/Library/Frameworks/%s.framework" % framework) or
            os.path.isdir("/Developer/Library/Frameworks/%s.framework" % framework) or
            os.path.isdir("/Users/%s/System/Library/Frameworks/%s.framework" % (getpass.getuser(), framework))):
            LibName(target_pkg, "-framework " + framework)
            for d, v in defs.values():
                DefSymbol(target_pkg, d, v)
        elif (pkg in PkgListGet()):
            print "%sWARNING:%s Could not locate framework %s, excluding from build" % (GetColor("red"), GetColor(), framework)
            PkgDisable(pkg)
        else:
            print "%sERROR:%s Could not locate framework %s, aborting build" % (GetColor("red"), GetColor(), framework)
            exit()
        return
    elif (LocateBinary(tool) != None and (tool != "pkg-config" or pkgconfig != None)):
        if (isinstance(pkgconfig, str) or tool != "pkg-config"):
            if (PkgConfigHavePkg(pkgconfig, tool)):
                return PkgConfigEnable(target_pkg, pkgconfig, tool)
        else:
            have_all_pkgs = True
            for pc in pkgconfig:
                if (PkgConfigHavePkg(pc, tool)):
                    PkgConfigEnable(target_pkg, pc, tool)
                else:
                    have_all_pkgs = False
            if (have_all_pkgs):
                return

    if (pkgconfig != None and (libs == None or len(libs) == 0)):
        if (pkg in PkgListGet()):
            print "%sWARNING:%s Could not locate pkg-config package %s, excluding from build" % (GetColor("red"), GetColor(), pkgconfig)
            PkgDisable(pkg)
        else:
            print "%sERROR:%s Could not locate pkg-config package %s, aborting build" % (GetColor("red"), GetColor(), pkgconfig)
            exit()
    else:
        # Okay, our pkg-config attempts failed. Let's try locating the libs by ourselves.
        have_pkg = True
        for l in libs:
            libname = l
            if (l.startswith("lib")):
                libname = l[3:]
            if (libname in GetLibCache()):
                LibName(target_pkg, "-l" + libname)
            else:
                if (VERBOSE):
                    print GetColor("cyan") + "Couldn't find library lib" + libname + GetColor()
                have_pkg = False

        for i in incs:
            incdir = None
            if (len(glob.glob("/usr/include/" + i)) > 0):
                incdir = sorted(glob.glob("/usr/include/" + i))[-1]
            elif (len(glob.glob("/usr/local/include/" + i)) > 0):
                incdir = sorted(glob.glob("/usr/local/include/" + i))[-1]
            elif (os.path.isdir("/usr/PCBSD") and len(glob.glob("/usr/PCBSD/local/include/" + i)) > 0):
                incdir = sorted(glob.glob("/usr/PCBSD/local/include/" + i))[-1]
            else:
                have_pkg = False
                # Try searching in the package's IncDirectories.
                for ppkg, pdir in INCDIRECTORIES:
                    if (pkg == ppkg and len(glob.glob(os.path.join(pdir, i))) > 0):
                        incdir = sorted(glob.glob(os.path.join(pdir, i)))[-1]
                        have_pkg = True
                if (incdir == None and VERBOSE and i.endswith(".h")):
                    print GetColor("cyan") + "Couldn't find header file " + i + GetColor()

            # Note: It's possible to specify a file instead of a dir, for the sake of checking if it exists.
            if (incdir != None and os.path.isdir(incdir)):
                IncDirectory(target_pkg, incdir)

        if (not have_pkg):
            if (pkg in PkgListGet()):
                print "%sWARNING:%s Could not locate thirdparty package %s, excluding from build" % (GetColor("red"), GetColor(), pkg.lower())
                PkgDisable(pkg)
            else:
                print "%sERROR:%s Could not locate thirdparty package %s, aborting build" % (GetColor("red"), GetColor(), pkg.lower())
                exit()

########################################################################
##
## SDK Location
##
## This section is concerned with locating the install directories
## for various third-party packages.  The results are stored in the
## SDK table.
##
## Microsoft keeps changing the &*#$*& registry key for the DirectX SDK.
## The only way to reliably find it is to search through the installer's
## uninstall-directories, look in each one, and see if it contains the
## relevant files.
##
########################################################################

SDK = {}

def GetSdkDir(sdkname, sdkkey = None):
    # Returns the default SDK directory. If it exists,
    # and sdkkey is not None, it is put in SDK[sdkkey].
    # Note: return value may not be an existing path.
    sdkbase = "sdks"
    if "MAKEPANDA_SDKS" in os.environ:
        sdkbase = os.environ["MAKEPANDA_SDKS"]

    sdir = sdkbase[:]
    if (sys.platform.startswith("win")):
        sdir += "/win"
        sdir += platform.architecture()[0][:2]
    elif (sys.platform.startswith("linux")):
        sdir += "/linux"
        sdir += platform.architecture()[0][:2]
    elif (sys.platform == "darwin"):
        sdir += "/macosx"
    sdir += "/" + sdkname

    # If it does not exist, try the old location.
    if (not os.path.isdir(sdir)):
        sdir = sdkbase + "/" + sdir
        if (sys.platform.startswith("linux")):
            sdir += "-linux"
            sdir += platform.architecture()[0][:2]
        elif (sys.platform == "darwin"):
            sdir += "-osx"

    if (sdkkey and os.path.isdir(sdir)):
        SDK[sdkkey] = sdir

    return sdir

def SdkLocateDirectX( strMode = 'default' ):
    if (sys.platform != "win32"): return
    if strMode == 'default':
        GetSdkDir("directx8", "DX8")
        GetSdkDir("directx9", "DX9")
        if ("DX9" not in SDK):
            strMode = 'latest'
    if strMode == 'latest':
        ## We first try to locate the August SDK in 64 bits, then 32.
        if ("DX9" not in SDK):
            dir = GetRegistryKey("SOFTWARE\\Wow6432Node\\Microsoft\\DirectX\\Microsoft DirectX SDK (June 2010)", "InstallPath")
            if (dir != 0):
                print "Using DirectX SDK June 2010"
                SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
                SDK["GENERIC_DXERR_LIBRARY"] = 1;
        if ("DX9" not in SDK):
            dir = GetRegistryKey("SOFTWARE\\Microsoft\\DirectX\\Microsoft DirectX SDK (June 2010)", "InstallPath")
            if (dir != 0):
                print "Using DirectX SDK June 2010"
                SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
                SDK["GENERIC_DXERR_LIBRARY"] = 1;
        if ("DX9" not in SDK):
            dir = GetRegistryKey("SOFTWARE\\Wow6432Node\\Microsoft\\DirectX\\Microsoft DirectX SDK (August 2009)", "InstallPath")
            if (dir != 0):
                print "Using DirectX SDK Aug 2009"
                SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
                SDK["GENERIC_DXERR_LIBRARY"] = 1;
        if ("DX9" not in SDK):
            dir = GetRegistryKey("SOFTWARE\\Microsoft\\DirectX\\Microsoft DirectX SDK (August 2009)", "InstallPath")
            if (dir != 0):
                print "Using DirectX SDK Aug 2009"
                SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
                SDK["GENERIC_DXERR_LIBRARY"] = 1;
        if ("DX9" not in SDK):
            ## Try to locate the key within the "new" March 2009 location in the registry (yecch):
            dir = GetRegistryKey("SOFTWARE\\Microsoft\\DirectX\\Microsoft DirectX SDK (March 2009)", "InstallPath")
            if (dir != 0):
                print "Using DirectX SDK March 2009"
                SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
        archStr = "x86"
        if (is_64): archStr = "x64"
        if ("DX9" not in SDK) or ("DX8" not in SDK):
            uninstaller = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
            for subdir in ListRegistryKeys(uninstaller):
                if (subdir[0]=="{"):
                    dir = GetRegistryKey(uninstaller+"\\"+subdir, "InstallLocation")
                    if (dir != 0):
                        if (("DX8" not in SDK) and
                            (os.path.isfile(dir+"\\Include\\d3d8.h")) and
                            (os.path.isfile(dir+"\\Include\\d3dx8.h")) and
                            (os.path.isfile(dir+"\\Lib\\d3d8.lib")) and
                            (os.path.isfile(dir+"\\Lib\\d3dx8.lib"))):
                            SDK["DX8"] = dir.replace("\\", "/").rstrip("/")
                        if (("DX9" not in SDK) and
                            (os.path.isfile(dir+"\\Include\\d3d9.h")) and
                            (os.path.isfile(dir+"\\Include\\d3dx9.h")) and
                            (os.path.isfile(dir+"\\Include\\dxsdkver.h")) and
                            (os.path.isfile(dir+"\\Lib\\" + archStr + "\\d3d9.lib")) and
                            (os.path.isfile(dir+"\\Lib\\" + archStr + "\\d3dx9.lib"))):
                            SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
        if ("DX9" not in SDK):
            return
    elif strMode == 'jun2010':
        if ("DX9" not in SDK):
            dir = GetRegistryKey("SOFTWARE\\Wow6432Node\\Microsoft\\DirectX\\Microsoft DirectX SDK (June 2010)", "InstallPath")
            if (dir != 0):
                print "Found DirectX SDK June 2010"
                SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
                SDK["GENERIC_DXERR_LIBRARY"] = 1;
        if ("DX9" not in SDK):
            dir = GetRegistryKey("SOFTWARE\\Microsoft\\DirectX\\Microsoft DirectX SDK (June 2010)", "InstallPath")
            if (dir != 0):
                print "Found DirectX SDK June 2010"
                SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
                SDK["GENERIC_DXERR_LIBRARY"] = 1;
        if ("DX9" not in SDK):
            exit("Couldn't find DirectX June2010 SDK")
    elif strMode == 'aug2009':
        if ("DX9" not in SDK):
            dir = GetRegistryKey("SOFTWARE\\Wow6432Node\\Microsoft\\DirectX\\Microsoft DirectX SDK (August 2009)", "InstallPath")
            if (dir != 0):
                print "Found DirectX SDK Aug 2009"
                SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
                SDK["GENERIC_DXERR_LIBRARY"] = 1;
        if ("DX9" not in SDK):
            dir = GetRegistryKey("SOFTWARE\\Microsoft\\DirectX\\Microsoft DirectX SDK (August 2009)", "InstallPath")
            if (dir != 0):
                print "Found DirectX SDK Aug 2009"
                SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
                SDK["GENERIC_DXERR_LIBRARY"] = 1;
        if ("DX9" not in SDK):
            exit("Couldn't find DirectX Aug 2009 SDK")
    elif strMode == 'mar2009':
        if ("DX9" not in SDK):
            ## Try to locate the key within the "new" March 2009 location in the registry (yecch):
            dir = GetRegistryKey("SOFTWARE\\Microsoft\\DirectX\\Microsoft DirectX SDK (March 2009)", "InstallPath")
            if (dir != 0):
                print "Found DirectX SDK March 2009"
                SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
        if ("DX9" not in SDK):
            exit("Couldn't find DirectX March 2009 SDK")
    elif strMode == 'aug2006':
        archStr = "x86"
        if (is_64): archStr = "x64"
        if ("DX9" not in SDK) or ("DX8" not in SDK):
            uninstaller = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
            for subdir in ListRegistryKeys(uninstaller):
                if (subdir[0]=="{"):
                    dir = GetRegistryKey(uninstaller+"\\"+subdir, "InstallLocation")
                    if (dir != 0):
                        if (("DX8" not in SDK) and
                            (os.path.isfile(dir+"\\Include\\d3d8.h")) and
                            (os.path.isfile(dir+"\\Include\\d3dx8.h")) and
                            (os.path.isfile(dir+"\\Lib\\d3d8.lib")) and
                            (os.path.isfile(dir+"\\Lib\\d3dx8.lib"))):
                            SDK["DX8"] = dir.replace("\\", "/").rstrip("/")
                        if (("DX9" not in SDK) and
                            (os.path.isfile(dir+"\\Include\\d3d9.h")) and
                            (os.path.isfile(dir+"\\Include\\d3dx9.h")) and
                            (os.path.isfile(dir+"\\Include\\dxsdkver.h")) and
                            (os.path.isfile(dir+"\\Lib\\" + archStr + "\\d3d9.lib")) and
                            (os.path.isfile(dir+"\\Lib\\" + archStr + "\\d3dx9.lib"))):
                            SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
        if ("DX9" not in SDK):
            exit("Couldn't find a DirectX Aug 2006 SDK")
    if ("DX9" in SDK):
        SDK["DIRECTCAM"] = SDK["DX9"]

def SdkLocateMaya():
    for (ver,key) in MAYAVERSIONINFO:
        if (PkgSkip(ver)==0 and ver not in SDK):
            GetSdkDir(ver.lower().replace("x",""), ver)
            if (not ver in SDK):
                if (sys.platform == "win32"):
                    for dev in ["Alias|Wavefront","Alias","Autodesk"]:
                        fullkey="SOFTWARE\\"+dev+"\\Maya\\"+key+"\\Setup\\InstallPath"
                        res = GetRegistryKey(fullkey, "MAYA_INSTALL_LOCATION", override64=False)
                        if (res != 0):
                            res = res.replace("\\", "/").rstrip("/")
                            SDK[ver] = res
                elif (sys.platform == "darwin"):
                    ddir = "/Applications/Autodesk/maya"+key
                    if (os.path.isdir(ddir)): SDK[ver] = ddir
                else:
                    if (is_64):
                        ddir1 = "/usr/autodesk/maya"+key+"-x64"
                        ddir2 = "/usr/aw/maya"+key+"-x64"
                    else:
                        ddir1 = "/usr/autodesk/maya"+key
                        ddir2 = "/usr/aw/maya"+key

                    if (os.path.isdir(ddir1)):   SDK[ver] = ddir1
                    elif (os.path.isdir(ddir2)): SDK[ver] = ddir2

def SdkLocateMax():
    if (sys.platform != "win32"): return
    for version,key1,key2,subdir in MAXVERSIONINFO:
        if (PkgSkip(version)==0):
            if (version not in SDK):
                GetSdkDir("maxsdk"+version.lower()[3:], version)
                GetSdkDir("maxsdk"+version.lower()[3:], version+"CS")
                if (not version in SDK):
                    top = GetRegistryKey(key1,key2)
                    if (top != 0):
                        SDK[version] = top + "maxsdk"
                        if (os.path.isdir(top + "\\" + subdir)!=0):
                            SDK[version+"CS"] = top + subdir

def SdkLocatePython(force_use_sys_executable = False):
    if (PkgSkip("PYTHON")==0):
        if (sys.platform == "win32" and not force_use_sys_executable):
            SDK["PYTHON"] = GetThirdpartyBase()+"/win-python"
            if (GetOptimize() <= 2):
                SDK["PYTHON"] += "-dbg"
            if (is_64 and os.path.isdir(SDK["PYTHON"] + "-x64")):
                SDK["PYTHON"] += "-x64"

            SDK["PYTHONEXEC"] = SDK["PYTHON"] + "/python"
            if (GetOptimize() <= 2): SDK["PYTHONEXEC"] += "_d.exe"
            else: SDK["PYTHONEXEC"] += ".exe"

            if (not os.path.isfile(SDK["PYTHONEXEC"])):
                exit("Could not find %s!" % SDK["PYTHONEXEC"])

            os.system(SDK["PYTHONEXEC"].replace("/", "\\") + " -V > "+OUTPUTDIR+"/tmp/pythonversion 2>&1")
            pv=ReadFile(OUTPUTDIR+"/tmp/pythonversion")
            if (pv.startswith("Python ")==0):
                exit("python -V did not produce the expected output")
            pv = pv[7:10]
            SDK["PYTHONVERSION"]="python"+pv

        elif (sys.platform == "win32"):
            SDK["PYTHON"] = os.path.dirname(sysconfig.get_python_inc())
            SDK["PYTHONVERSION"] = "python" + sysconfig.get_python_version()
            SDK["PYTHONEXEC"] = sys.executable
        else:
            SDK["PYTHON"] = sysconfig.get_python_inc()
            SDK["PYTHONVERSION"] = "python" + sysconfig.get_python_version()
            if (os.path.islink(sys.executable)):
                SDK["PYTHONEXEC"] = os.path.join(os.path.dirname(sys.executable), os.readlink(sys.executable))
            else:
                SDK["PYTHONEXEC"] = sys.executable
    elif (os.path.islink(sys.executable)):
        SDK["PYTHONEXEC"] = os.path.join(os.path.dirname(sys.executable), os.readlink(sys.executable))
    else:
        SDK["PYTHONEXEC"] = sys.executable

def SdkLocateVisualStudio():
    if (sys.platform != "win32"): return
    vcdir = GetRegistryKey("SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VC7", "9.0")
    if (vcdir != 0) and (vcdir[-4:] == "\\VC\\"):
        vcdir = vcdir[:-3]
        SDK["VISUALSTUDIO"] = vcdir
    elif "VCINSTALLDIR" in os.environ:
        vcdir = os.environ["VCINSTALLDIR"]
        if (vcdir[-3:] == "\\VC"):
            vcdir = vcdir[:-2]
        elif (vcdir[-4:] == "\\VC\\"):
            vcdir = vcdir[:-3]
        SDK["VISUALSTUDIO"] = vcdir

def SdkLocateMSPlatform( strMode = 'default'):
    if (sys.platform != "win32"): return
    platsdk = 0
    if (strMode == 'default'):
        platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v7.1", "InstallationFolder")
        if (platsdk and not os.path.isdir(platsdk)):
            platsdk = 0
            
        if (platsdk == 0):
            platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\MicrosoftSDK\\InstalledSDKs\\D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1", "Install Dir")
            if (platsdk and not os.path.isdir(platsdk)): 
                platsdk = 0
                
        if (platsdk == 0):
            platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v6.1","InstallationFolder")
            if (platsdk and not os.path.isdir(platsdk)): 
                platsdk = 0
                
        if (platsdk == 0):
            platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v6.0A","InstallationFolder")
            if (platsdk and not os.path.isdir(platsdk)):           
                platsdk = 0
                
        if (platsdk == 0 and os.path.isdir(os.path.join(GetProgramFiles(), "Microsoft Platform SDK for Windows Server 2003 R2"))):
            if (not is_64 or os.path.isdir(os.path.join(GetProgramFiles(), "Microsoft Platform SDK for Windows Server 2003 R2", "Lib", "AMD64"))):
                platsdk = os.path.join(GetProgramFiles(), "Microsoft Platform SDK for Windows Server 2003 R2")
                if (not os.path.isdir(platsdk)): 
                    platsdk = 0

        # Doesn't work with the Express versions, so we're checking for the "atlmfc" dir, which is not in the Express
        if (platsdk == 0 and os.path.isdir(os.path.join(GetProgramFiles(), "Microsoft Visual Studio 9\\VC\\atlmfc"))
                         and os.path.isdir(os.path.join(GetProgramFiles(), "Microsoft Visual Studio 9\\VC\\PlatformSDK"))):
            platsdk = os.path.join(GetProgramFiles(), "Microsoft Visual Studio 9\\VC\\PlatformSDK")
            if (not os.path.isdir(platsdk)): 
                platsdk = 0

        # This may not be the best idea but it does give a warning
        if (platsdk == 0):
            if ("WindowsSdkDir" in os.environ):
                WARNINGS.append("Windows SDK directory not found in registry, found in Environment variables instead")
                platsdk = os.environ["WindowsSdkDir"]
                
    elif (strMode == 'win71'):
        platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v7.1", "InstallationFolder")
        if (platsdk and not os.path.isdir(platsdk)):
            platsdk = 0
        if not platsdk:
            exit("Couldn't find Win7.1 Platform SDK")
    elif (strMode == 'win61'):
        platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v6.1","InstallationFolder")
        if (platsdk and not os.path.isdir(platsdk)):
            platsdk = 0
        if not platsdk:
            exit("Couldn't find Win6.1 Platform SDK")
    elif (strMode == 'win60A'):
        platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v6.0A","InstallationFolder")
        if (platsdk and not os.path.isdir(platsdk)):           
            platsdk = 0
        if not platsdk:
            exit("Couldn't find Win6.0 Platform SDK")
    elif (strMode == 'winserver2003r2'):
        platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\MicrosoftSDK\\InstalledSDKs\\D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1", "Install Dir")
        if (platsdk and not os.path.isdir(platsdk)): 
            platsdk = 0
        if (platsdk == 0 and os.path.isdir(os.path.join(GetProgramFiles(), "Microsoft Platform SDK for Windows Server 2003 R2"))):
            if (not is_64 or os.path.isdir(os.path.join(GetProgramFiles(), "Microsoft Platform SDK for Windows Server 2003 R2", "Lib", "AMD64"))):
                platsdk = os.path.join(GetProgramFiles(), "Microsoft Platform SDK for Windows Server 2003 R2")
                if (not os.path.isdir(platsdk)): 
                    platsdk = 0
        if (platsdk == 0 and os.path.isdir("C:/Program Files/Microsoft Platform SDK for Windows Server 2003 R2")):
            if (not is_64 or os.path.isdir(os.path.join("C:/Program Files/Microsoft Platform SDK for Windows Server 2003 R2", "Lib", "AMD64"))):
                platsdk = os.path.join("C:/Program Files/Microsoft Platform SDK for Windows Server 2003 R2")
                if (not os.path.isdir(platsdk)): 
                    platsdk = 0
        if not platsdk:
            exit("Couldn't find Windows Server 2003 R2 PlatformSDK")                
                
    if (platsdk != 0):
        if (not platsdk.endswith("\\")):
            platsdk += "\\"
        SDK["MSPLATFORM"] = platsdk

def SdkLocateMacOSX(osxtarget = None):
    if (sys.platform != "darwin"): return
    if (osxtarget != None):
        if (os.path.exists("/Developer/SDKs/MacOSX%su.sdk" % osxtarget)):
            SDK["MACOSX"] = "/Developer/SDKs/MacOSX%su.sdk" % osxtarget
        elif (os.path.exists("/Developer/SDKs/MacOSX%s.sdk" % osxtarget)):
            SDK["MACOSX"] = "/Developer/SDKs/MacOSX%s.sdk" % osxtarget
        elif (os.path.exists("/Developer/SDKs/MacOSX%s.0.sdk" % osxtarget)):
            SDK["MACOSX"] = "/Developer/SDKs/MacOSX%s.0.sdk" % osxtarget
        else:
            exit("Couldn't find any MacOSX SDK for OSX version %s!" % osxtarget)
    else:
        SDK["MACOSX"] = ""

# Latest first
PHYSXVERSIONINFO=[
    ("PHYSX284","v2.8.4"),
    ("PHYSX283","v2.8.3"),
    ("PHYSX281","v2.8.1"),
]

def SdkLocatePhysX():
    # First check for a physx directory in sdks.
    dir = GetSdkDir("physx")
    if (dir and os.path.isdir(dir)):
        SDK["PHYSX"] = dir
        SDK["PHYSXLIBS"] = dir + "/lib"
        return

    # Try to find a PhysX installation on the system.
    for (ver, key) in PHYSXVERSIONINFO:
        if (sys.platform == "win32"):
            folders = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\Folders"
            for folder in ListRegistryValues(folders):
                if folder.endswith("NVIDIA PhysX SDK\\%s\\SDKs\\" % key) or \
                   folder.endswith("NVIDIA PhysX SDK\\%s_win\\SDKs\\" % key):
                    SDK["PHYSX"] = folder
                    return
        elif (sys.platform.startswith("linux")):
            incpath = "/usr/include/PhysX/%s/SDKs" % key
            libpath = "/usr/lib/PhysX/%s" % key
            if (os.path.isdir(incpath) and os.path.isdir(libpath)):
                SDK["PHYSX"] = incpath
                SDK["PHYSXLIBS"] = libpath
                return

def SdkLocateSpeedTree():
    # Look for all of the SpeedTree SDK directories within the
    # sdks/win32/speedtree dir, and pick the highest-numbered one.
    dir = GetSdkDir("speedtree")
    if not os.path.exists(dir):
        return

    speedtrees = []
    for dirname in os.listdir(dir):
        if dirname.startswith('SpeedTree SDK v'):
            version = dirname[15:].split()[0]
            version = map(int, version.split('.'))
            speedtrees.append((version, dirname))
    if not speedtrees:
        # No installed SpeedTree SDK.
        return

    speedtrees.sort()
    version, dirname = speedtrees[-1]
    SDK["SPEEDTREE"] = os.path.join(dir, dirname)
    SDK["SPEEDTREEAPI"] = "OpenGL"
    SDK["SPEEDTREEVERSION"] = '%s.%s' % (version[0], version[1])

########################################################################
##
## SDK Auto-Disables
##
## Disable packages whose SDKs could not be found.
##
########################################################################

def SdkAutoDisableDirectX():
    for ver in ["DX8","DX9","DIRECTCAM"]:
        if (PkgSkip(ver)==0):
            if (ver not in SDK):
                if (sys.platform.startswith("win")):
                    WARNINGS.append("I cannot locate SDK for "+ver)
                    WARNINGS.append("I have automatically added this command-line option: --no-"+ver.lower())
                PkgDisable(ver)
            else:
                WARNINGS.append("Using "+ver+" sdk: "+SDK[ver])

def SdkAutoDisableMaya():
    for (ver,key) in MAYAVERSIONINFO:
        if (ver not in SDK) and (PkgSkip(ver)==0):
            if (sys.platform == "win32"):
                WARNINGS.append("The registry does not appear to contain a pointer to the "+ver+" SDK.")
            else:
                WARNINGS.append("I cannot locate SDK for "+ver)
            WARNINGS.append("I have automatically added this command-line option: --no-"+ver.lower())
            PkgDisable(ver)

def SdkAutoDisableMax():
    for version,key1,key2,subdir in MAXVERSIONINFO:
        if (PkgSkip(version)==0) and ((version not in SDK) or (version+"CS" not in SDK)):
            if (sys.platform.startswith("win")):
                if (version in SDK):
                    WARNINGS.append("Your copy of "+version+" does not include the character studio SDK")
                else:
                    WARNINGS.append("The registry does not appear to contain a pointer to "+version)
                WARNINGS.append("I have automatically added this command-line option: --no-"+version.lower())
            PkgDisable(version)

def SdkAutoDisablePhysX():
    if ("PHYSX" not in SDK) and (PkgSkip("PHYSX")==0):
        PkgDisable("PHYSX")
        WARNINGS.append("I cannot locate SDK for PhysX")
        WARNINGS.append("I have automatically added this command-line option: --no-physx")

def SdkAutoDisableSpeedTree():
    if ("SPEEDTREE" not in SDK) and (PkgSkip("SPEEDTREE")==0):
        PkgDisable("SPEEDTREE")
        WARNINGS.append("I cannot locate SDK for SpeedTree")
        WARNINGS.append("I have automatically added this command-line option: --no-speedtree")

########################################################################
##
## Visual Studio comes with a script called VSVARS32.BAT, which
## you need to run before using visual studio command-line tools.
## The following python subroutine serves the same purpose.
##
########################################################################

def AddToPathEnv(path,add):
    if (path in os.environ):
        if (sys.platform.startswith("win")):
            os.environ[path] = add + ";" + os.environ[path]
        else:
            os.environ[path] = add + ":" + os.environ[path]
    else:
        os.environ[path] = add

def SetupVisualStudioEnviron():
    if ("VISUALSTUDIO" not in SDK):
        exit("Could not find Visual Studio install directory")
    if ("MSPLATFORM" not in SDK):
        exit("Could not find the Microsoft Platform SDK")
    os.environ["VCINSTALLDIR"] = SDK["VISUALSTUDIO"] + "VC"
    os.environ["WindowsSdkDir"] = SDK["MSPLATFORM"]
    suffix=""
    if (is_64): suffix = "\\amd64"
    AddToPathEnv("PATH",    SDK["VISUALSTUDIO"] + "VC\\bin"+suffix)
    AddToPathEnv("PATH",    SDK["VISUALSTUDIO"] + "Common7\\IDE")
    AddToPathEnv("INCLUDE", SDK["VISUALSTUDIO"] + "VC\\include")
    AddToPathEnv("INCLUDE", SDK["VISUALSTUDIO"] + "VC\\atlmfc\\include")
    AddToPathEnv("LIB",     SDK["VISUALSTUDIO"] + "VC\\lib"+suffix)
    AddToPathEnv("LIB",     SDK["VISUALSTUDIO"] + "VC\\atlmfc\\lib")
    AddToPathEnv("PATH",    SDK["MSPLATFORM"] + "bin")
    AddToPathEnv("INCLUDE", SDK["MSPLATFORM"] + "include")
    AddToPathEnv("INCLUDE", SDK["MSPLATFORM"] + "include\\atl")
    AddToPathEnv("INCLUDE", SDK["MSPLATFORM"] + "include\\mfc")
    if (not is_64):
        AddToPathEnv("LIB", SDK["MSPLATFORM"] + "lib")
        AddToPathEnv("PATH",SDK["VISUALSTUDIO"] + "VC\\redist\\x86\\Microsoft.VC90.CRT")
        AddToPathEnv("PATH",SDK["VISUALSTUDIO"] + "VC\\redist\\x86\\Microsoft.VC90.MFC")
    elif (os.path.isdir(SDK["MSPLATFORM"] + "lib\\x64")):
        AddToPathEnv("LIB", SDK["MSPLATFORM"] + "lib\\x64")
    elif (os.path.isdir(SDK["MSPLATFORM"] + "lib\\amd64")):
        AddToPathEnv("LIB", SDK["MSPLATFORM"] + "lib\\amd64")
    else:
        exit("Could not locate 64-bits libraries in Platform SDK!")

########################################################################
#
# Include and Lib directories.
#
# These allow you to add include and lib directories to the
# compiler search paths.  These methods accept a "package"
# parameter, which specifies which package the directory is
# associated with.  The include/lib directory is not used
# if the package is not selected.  The package can be 'ALWAYS'.
#
########################################################################

INCDIRECTORIES = []
LIBDIRECTORIES = []
LIBNAMES = []
DEFSYMBOLS = []

def IncDirectory(opt, dir):
    INCDIRECTORIES.append((opt, dir))

def LibDirectory(opt, dir):
    LIBDIRECTORIES.append((opt, dir))

def LibName(opt, name):
    # Check to see if the lib file actually exists for the thirdparty library given
    # Are we a thirdparty library?
    if name.startswith(GetThirdpartyDir()):
        # Does this lib exist?
        if not os.path.exists(name):
            WARNINGS.append(name + " not found.  Skipping Package " + opt)
            if (opt in PkgListGet()):
                print "%sWARNING:%s Could not locate thirdparty package %s, excluding from build" % (GetColor("red"), GetColor(), opt.lower())
                PkgDisable(opt)
                return
            else:
                print "%sERROR:%s Could not locate thirdparty package %s, aborting build" % (GetColor("red"), GetColor(), opt.lower())
                exit()
    LIBNAMES.append((opt, name))

def DefSymbol(opt, sym, val):
    DEFSYMBOLS.append((opt, sym, val))

########################################################################
#
# On Linux/OSX, to run panda, the dynamic linker needs to know how to
# find the shared libraries.  This subroutine verifies that the dynamic
# linker is properly configured.  If not, it sets it up on a temporary
# basis and issues a warning.
#
########################################################################

def CheckLinkerLibraryPath():
    if (sys.platform == "win32"): return
    builtlib = os.path.abspath(os.path.join(OUTPUTDIR,"lib"))
    dyldpath = []
    try:
        ldpath = []
        f = file("/etc/ld.so.conf","r")
        for line in f: ldpath.append(line.rstrip())
        f.close()
    except: ldpath = []

    # Get the current
    if ("LD_LIBRARY_PATH" in os.environ):
        ldpath = ldpath + os.environ["LD_LIBRARY_PATH"].split(":")
    if (sys.platform == "darwin" and "DYLD_LIBRARY_PATH" in os.environ):
        dyldpath = os.environ["DYLD_LIBRARY_PATH"].split(":")

    # Remove any potential current Panda installation lib dirs
    for i in ldpath:
        if i.startswith("/usr/lib/panda"): ldpath.remove(i)
    for i in ldpath:
        if i.startswith("/usr/local/panda"): ldpath.remove(i)
    for i in dyldpath:
        if i.startswith("/Applications/Panda3D"): dyldpath.remove(i)
        if i.startswith("/Developer/Panda3D"): dyldpath.remove(i)

    # Add built/lib/ to (DY)LD_LIBRARY_PATH if it's not already there
    if (ldpath.count(builtlib)==0):
        if ("LD_LIBRARY_PATH" in os.environ):
            os.environ["LD_LIBRARY_PATH"] = builtlib + ":" + os.environ["LD_LIBRARY_PATH"]
        else:
            os.environ["LD_LIBRARY_PATH"] = builtlib
    if (sys.platform == "darwin" and dyldpath.count(builtlib)==0):
        if ("DYLD_LIBRARY_PATH" in os.environ):
            os.environ["DYLD_LIBRARY_PATH"] = builtlib + ":" + os.environ["DYLD_LIBRARY_PATH"]
        else:
            os.environ["DYLD_LIBRARY_PATH"] = builtlib

    # Workaround around compile issue on PCBSD
    if (os.path.exists("/usr/PCBSD")):
        os.environ["LD_LIBRARY_PATH"] += ":/usr/PCBSD/local/lib"

########################################################################
##
## Routines to copy files into the build tree
##
########################################################################

def CopyFile(dstfile, srcfile):
    if (dstfile[-1]=='/'):
        dstdir = dstfile
        fnl = srcfile.rfind("/")
        if (fnl < 0): fn = srcfile
        else: fn = srcfile[fnl+1:]
        dstfile = dstdir + fn
    if (NeedsBuild([dstfile],[srcfile])):
        WriteFile(dstfile,ReadFile(srcfile))
        JustBuilt([dstfile], [srcfile])

def CopyAllFiles(dstdir, srcdir, suffix=""):
    for x in GetDirectoryContents(srcdir, ["*"+suffix]):
        CopyFile(dstdir+x, srcdir+x)

def CopyAllHeaders(dir, skip=[]):
    for filename in GetDirectoryContents(dir, ["*.h", "*.I", "*.T"], skip):
        srcfile = dir + "/" + filename
        dstfile = OUTPUTDIR+"/include/" + filename
        if (NeedsBuild([dstfile],[srcfile])):
            WriteFile(dstfile,ReadFile(srcfile))
            JustBuilt([dstfile],[srcfile])

def CopyTree(dstdir, srcdir, omitCVS=True):
    if (os.path.isdir(dstdir)):
        for entry in os.listdir(srcdir):
            srcpth = os.path.join(srcdir, entry)
            dstpth = os.path.join(dstdir, entry)
            if (os.path.isfile(srcpth)):
                if (not omitCVS or entry != ".cvsignore"):
                    CopyFile(dstpth, srcpth)
            else:
                if (not omitCVS or entry != "CVS"):
                    CopyTree(dstpth, srcpth)
    else:
        if (sys.platform == "win32"):
            cmd = 'xcopy /I/Y/E/Q "' + srcdir + '" "' + dstdir + '"'
        else:
            cmd = 'cp -R -f ' + srcdir + ' ' + dstdir
        oscmd(cmd)

########################################################################
##
## Parse PandaVersion.pp to extract the version number.
##
########################################################################

def ParsePandaVersion(fn):
    try:
        f = file(fn, "r")
        pattern = re.compile('^[ \t]*[#][ \t]*define[ \t]+PANDA_VERSION[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)')
        for line in f:
            match = pattern.match(line,0)
            if (match):
                f.close()
                return match.group(1)+"."+match.group(2)+"."+match.group(3)
        f.close()
    except: pass
    return "0.0.0"

def ParsePluginVersion(fn):
    try:
        f = file(fn, "r")
        pattern = re.compile('^[ \t]*[#][ \t]*define[ \t]+P3D_PLUGIN_VERSION[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)')
        for line in f:
            match = pattern.match(line,0)
            if (match):
                f.close()
                return match.group(1)+"."+match.group(2)+"."+match.group(3)
        f.close()
    except: pass
    return "0.0.0"

def ParseCoreapiVersion(fn):
    try:
        f = file(fn, "r")
        pattern = re.compile('^[ \t]*[#][ \t]*define[ \t]+P3D_COREAPI_VERSION.*([0-9]+)[ \t]*$')
        for line in f:
            match = pattern.match(line,0)
            if (match):
                f.close()
                return match.group(1)
        f.close()
    except: pass
    return "0"

##########################################################################################
#
# Utility function to generate a resource file
#
##########################################################################################

RESOURCE_FILE_TEMPLATE = """VS_VERSION_INFO VERSIONINFO
 FILEVERSION %(commaversion)s
 PRODUCTVERSION %(commaversion)s
 FILEFLAGSMASK 0x3fL
 FILEFLAGS %(debugflag)s
 FILEOS 0x40004L
 FILETYPE 0x2L
 FILESUBTYPE 0x0L
BEGIN
    BLOCK "StringFileInfo"
    BEGIN
        BLOCK "040904e4"
        BEGIN
            VALUE "FileDescription", "%(description)s\\0"
            VALUE "FileVersion", "%(dotversion)s"
            VALUE "LegalTrademarks", "\\0"
            VALUE "MIMEType", "%(mimetype)s\\0"
            VALUE "FileExtents", "%(extension)s\\0"
            VALUE "FileOpenName", "%(filedesc)s\\0"
            VALUE "OLESelfRegister", "\\0"
            VALUE "OriginalFilename", "%(filename)s\\0"
            VALUE "ProductName", "%(name)s %(version)s\\0"
            VALUE "ProductVersion", "%(dotversion)s"
        END
    END
    BLOCK "VarFileInfo"
    BEGIN
        VALUE "Translation", 0x409, 1252
    END
END
"""

def GenerateResourceFile(**kwargs):
    if "debugflag" not in kwargs:
        if GetOptimize() <= 2:
            kwargs["debugflag"] = "0x1L"
        else:
            kwargs["debugflag"] = "0x0L"
    kwargs["dotversion"] = kwargs["version"]
    if len(kwargs["dotversion"].split(".")) == 3:
        kwargs["dotversion"] += ".0"
    if "commaversion" not in kwargs:
        kwargs["commaversion"] = kwargs["dotversion"].replace(".", ",")

    rcdata = ""
    if not "noinclude" in kwargs:
        rcdata += "#define APSTUDIO_READONLY_SYMBOLS\n"
        rcdata += "#include \"winresrc.h\"\n"
        rcdata += "#undef APSTUDIO_READONLY_SYMBOLS\n"
    rcdata += RESOURCE_FILE_TEMPLATE % kwargs

    if "icon" in kwargs:
        rcdata += "\nICON_FILE       ICON    \"%s\"\n" % kwargs["icon"]

    return rcdata


def WriteResourceFile(basename, **kwargs):
    if not basename.endswith(".rc"):
        basename += ".rc"
    basename = GetOutputDir() + "/include/" + basename
    ConditionalWriteFile(basename, GenerateResourceFile(**kwargs))
    return basename

########################################################################
##
## FindLocation
##
########################################################################

ORIG_EXT={}

def GetOrigExt(x):
    return ORIG_EXT[x]

def SetOrigExt(x, v):
    ORIG_EXT[x] = v

def CalcLocation(fn, ipath):
    if (fn.count("/")): return fn
    dllext = ""
    if (GetOptimize() <= 2 and sys.platform.startswith("win")): dllext = "_d"

    if (fn == "PandaModules.py"): return "pandac/" + fn
    if (fn.endswith(".cxx")): return CxxFindSource(fn, ipath)
    if (fn.endswith(".I")):   return CxxFindSource(fn, ipath)
    if (fn.endswith(".h")):   return CxxFindSource(fn, ipath)
    if (fn.endswith(".c")):   return CxxFindSource(fn, ipath)
    if (fn.endswith(".yxx")): return CxxFindSource(fn, ipath)
    if (fn.endswith(".lxx")): return CxxFindSource(fn, ipath)
    if (fn.endswith(".pdef")):return CxxFindSource(fn, ipath)
    if (fn.endswith(".egg")): return OUTPUTDIR+"/models/"+fn
    if (fn.endswith(".egg.pz")):return OUTPUTDIR+"/models/"+fn
    if (sys.platform.startswith("win")):
        if (fn.endswith(".def")):   return CxxFindSource(fn, ipath)
        if (fn.endswith(".rc")):    return CxxFindSource(fn, ipath)
        if (fn.endswith(".idl")):   return CxxFindSource(fn, ipath)
        if (fn.endswith(".obj")):   return OUTPUTDIR+"/tmp/"+fn
        if (fn.endswith(".res")):   return OUTPUTDIR+"/tmp/"+fn
        if (fn.endswith(".tlb")):   return OUTPUTDIR+"/tmp/"+fn
        if (fn.endswith(".dll")):   return OUTPUTDIR+"/bin/"+fn[:-4]+dllext+".dll"
        if (fn.endswith(".pyd")):   return OUTPUTDIR+"/bin/"+fn[:-4]+dllext+".pyd"
        if (fn.endswith(".ocx")):   return OUTPUTDIR+"/plugins/"+fn[:-4]+dllext+".ocx"
        if (fn.endswith(".mll")):   return OUTPUTDIR+"/plugins/"+fn[:-4]+dllext+".mll"
        if (fn.endswith(".dlo")):   return OUTPUTDIR+"/plugins/"+fn[:-4]+dllext+".dlo"
        if (fn.endswith(".dli")):   return OUTPUTDIR+"/plugins/"+fn[:-4]+dllext+".dli"
        if (fn.endswith(".dle")):   return OUTPUTDIR+"/plugins/"+fn[:-4]+dllext+".dle"
        if (fn.endswith(".plugin")):return OUTPUTDIR+"/plugins/"+fn[:-7]+dllext+".dll"
        if (fn.endswith(".exe")):   return OUTPUTDIR+"/bin/"+fn
        if (fn.endswith(".lib")):   return OUTPUTDIR+"/lib/"+fn[:-4]+dllext+".lib"
        if (fn.endswith(".ilb")):   return OUTPUTDIR+"/tmp/"+fn[:-4]+dllext+".lib"
        if (fn.endswith(".dat")):   return OUTPUTDIR+"/tmp/"+fn
        if (fn.endswith(".in")):    return OUTPUTDIR+"/pandac/input/"+fn
    elif (sys.platform == "darwin"):
        if (fn.endswith(".mm")):    return CxxFindSource(fn, ipath)
        if (fn.endswith(".r")):     return CxxFindSource(fn, ipath)
        if (fn.endswith(".plist")): return CxxFindSource(fn, ipath)
        if (fn.endswith(".obj")):   return OUTPUTDIR+"/tmp/"+fn[:-4]+".o"
        if (fn.endswith(".dll")):   return OUTPUTDIR+"/lib/"+fn[:-4]+".dylib"
        if (fn.endswith(".pyd")):   return OUTPUTDIR+"/lib/"+fn[:-4]+".so"
        if (fn.endswith(".mll")):   return OUTPUTDIR+"/plugins/"+fn
        if (fn.endswith(".exe")):   return OUTPUTDIR+"/bin/"+fn[:-4]
        if (fn.endswith(".lib")):   return OUTPUTDIR+"/lib/"+fn[:-4]+".a"
        if (fn.endswith(".ilb")):   return OUTPUTDIR+"/tmp/"+fn[:-4]+".a"
        if (fn.endswith(".dat")):   return OUTPUTDIR+"/tmp/"+fn
        if (fn.endswith(".rsrc")):  return OUTPUTDIR+"/tmp/"+fn
        if (fn.endswith(".plugin")):return OUTPUTDIR+"/plugins/"+fn
        if (fn.endswith(".app")):   return OUTPUTDIR+"/bin/"+fn
        if (fn.endswith(".in")):    return OUTPUTDIR+"/pandac/input/"+fn
    else:
        if (fn.endswith(".obj")):   return OUTPUTDIR+"/tmp/"+fn[:-4]+".o"
        if (fn.endswith(".dll")):   return OUTPUTDIR+"/lib/"+fn[:-4]+".so"
        if (fn.endswith(".pyd")):   return OUTPUTDIR+"/lib/"+fn[:-4]+".so"
        if (fn.endswith(".mll")):   return OUTPUTDIR+"/plugins/"+fn
        if (fn.endswith(".plugin")):return OUTPUTDIR+"/plugins/"+fn[:-7]+dllext+".so"
        if (fn.endswith(".exe")):   return OUTPUTDIR+"/bin/"+fn[:-4]
        if (fn.endswith(".lib")):   return OUTPUTDIR+"/lib/"+fn[:-4]+".a"
        if (fn.endswith(".ilb")):   return OUTPUTDIR+"/tmp/"+fn[:-4]+".a"
        if (fn.endswith(".dat")):   return OUTPUTDIR+"/tmp/"+fn
        if (fn.endswith(".in")):    return OUTPUTDIR+"/pandac/input/"+fn
    return fn


def FindLocation(fn, ipath):
    if (GetLinkAllStatic() and fn.endswith(".dll")):
        fn = fn[:-4]+".lib"
    loc = CalcLocation(fn, ipath)
    (base,ext) = os.path.splitext(fn)
    ORIG_EXT[loc] = ext
    return loc

########################################################################
##
## TargetAdd
##
## Makepanda maintains a list of make-targets.  Each target has
## these attributes:
##
## name   - the name of the file being created.
## ext    - the original file extension, prior to OS-specific translation
## inputs - the names of the input files to the compiler
## deps   - other input files that the target also depends on
## opts   - compiler options, a catch-all category
##
## TargetAdd will create the target if it does not exist.  Then,
## depending on what options you pass, it will push data onto these
## various target attributes.  This is cumulative: for example, if
## you use TargetAdd to add compiler options, then use TargetAdd
## again with more compiler options, both sets of options will be
## included.
##
## TargetAdd does some automatic dependency generation on C++ files.
## It will scan these files for include-files and automatically push
## the include files onto the list of dependencies.  In order to do
## this, it needs an include-file search path.  So if you supply
## any C++ input, you also need to supply compiler options containing
## include-directories, or alternately, a separate ipath parameter.
##
## The main body of 'makepanda' is a long list of TargetAdd
## directives building up a giant list of make targets.  Then,
## finally, the targets are run and panda is built.
##
## Makepanda's dependency system does not understand multiple
## outputs from a single build step.  When a build step generates
## a primary output file and a secondary output file, it is
## necessary to trick the dependency system.  Insert a dummy
## build step that "generates" the secondary output file, using
## the primary output file as an input.  There is a special
## compiler option DEPENDENCYONLY that creates such a dummy
## build-step.  There are two cases where dummy build steps must
## be inserted: bison generates an OBJ and a secondary header
## file, interrogate generates an IN and a secondary IGATE.OBJ.
##
########################################################################

class Target:
    pass

TARGET_LIST=[]
TARGET_TABLE={}

def TargetAdd(target, dummy=0, opts=0, input=0, dep=0, ipath=0, winrc=0):
    if (dummy != 0):
        exit("Syntax error in TargetAdd "+target)
    if (ipath == 0): ipath = opts
    if (ipath == 0): ipath = []
    if (type(input) == str): input = [input]
    if (type(dep) == str): dep = [dep]
    full = FindLocation(target,[OUTPUTDIR+"/include"])
    if (full not in TARGET_TABLE):
        t = Target()
        t.name = full
        t.inputs = []
        t.deps = {}
        t.opts = []
        TARGET_TABLE[full] = t
        TARGET_LIST.append(t)
    else:
        t = TARGET_TABLE[full]
    ipath = [OUTPUTDIR+"/tmp"] + GetListOption(ipath, "DIR:") + [OUTPUTDIR+"/include"]
    if (opts != 0):
        for x in opts:
            if (t.opts.count(x)==0):
                t.opts.append(x)
    if (input != 0):
        for x in input:
            fullinput = FindLocation(x, ipath)
            t.inputs.append(fullinput)
            # Don't re-link a library or binary if just it's dependency dll's have been altered.
            # This should work out fine in most cases, and often reduces recompilation time.
            if (os.path.splitext(x)[-1] not in SUFFIX_DLL):
                t.deps[fullinput] = 1
                (base,suffix) = os.path.splitext(x)
                if (SUFFIX_INC.count(suffix)):
                    for d in CxxCalcDependencies(fullinput, ipath, []):
                        t.deps[d] = 1
    if (dep != 0):
        for x in dep:
            fulldep = FindLocation(x, ipath)
            t.deps[fulldep] = 1
    if (winrc != 0 and sys.platform.startswith("win")):
        TargetAdd(target, input=WriteResourceFile(target.split("/")[-1].split(".")[0], **winrc))
    if (target.endswith(".in")):
        t.deps[FindLocation("interrogate.exe",[])] = 1
        t.deps[FindLocation("dtool_have_python.dat",[])] = 1
    if (target.endswith(".pz")):
        t.deps[FindLocation("pzip.exe",[])] = 1

if __name__ == '__main__':
    ##Debug SDK search check
    if sys.platform == "win32":
        SdkLocateDirectX( 'aug2006' )   
        SdkLocateMSPlatform( 'winserver2003r2')
         
